using Google.Protobuf;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using UnityEngine;

public interface INetEvent
{
    (byte[],int) Send(IMessage msg);
    void OnRead(MemoryBuffer buffer);
    void OnNetError(int errCode);
    void Update();
}

public class ClientNet : INetEvent
{
    long id;
    CodedInputStream inputStream;
    byte[] outputBuffer;
    CodedOutputStream outputStream;
    MsgQueue msgQueue;

    public ClientNet(long id)
    {
        this.id = id;
        inputStream = new CodedInputStream(System.Array.Empty<byte>());
        outputBuffer = new byte[4096];
        outputStream = new CodedOutputStream(outputBuffer);
        msgQueue = new MsgQueue(OnHandleMessage);
    }

    public void Destroy()
    {
        inputStream.Dispose();
        outputStream.Dispose();
        msgQueue.Dispose();
    }

    void OnHandleMessage(Google.Protobuf.IMessage msg)
    {
        if (MessageManager.TryGetMsgId(msg.GetType(), out int msgId))
        {
            if (MessageManager.TryGetNetEventHandler(msgId, out var handler))
            {
                handler(msg);
            }
            MessagePool.Instance.Recycle(msg);
        }
    }

    public void OnNetError(int errCode)
    {

    }

    public void OnRead(MemoryBuffer memoryBuffer)
    {
        var buf = memoryBuffer.GetBuffer();
        int msgId = buf.getMsgId();
        Debug.Log($"OnRead: sessionId=[{id}] msgId=[{msgId}] msgLength={memoryBuffer.Length - memoryBuffer.Position}");
        inputStream.Reset(buf, 8, (int)memoryBuffer.Length);
        if (!MessageManager.TryGetMsgType(msgId, out var msgType))
        {
            Debug.LogError($"Message With Id = {msgId} Has Not Registered Yet !!");
            return;
        }

        var msg = MessagePool.Instance.Fetch(msgType);
        msg.MergeFrom(inputStream);

        msgQueue.Enqueue(msg);
    }

    public (byte[],int) Send(IMessage msg)
    {
        if (MessageManager.TryGetMsgId(msg.GetType(), out var msgId))
        {
            outputBuffer.putMsgId(msgId);
            outputStream.Reset(this.outputBuffer, 8, this.outputBuffer.Length - 8);
            msg.WriteTo(outputStream);
            int msgLength = (int)outputStream.Position;
            outputBuffer.putMsgLength(msgLength);

            return (outputBuffer, msgLength);
        }
        return (null, 0);
    }

    public void Update()
    {
        msgQueue.DispatchNetEvent();
    }
}

public class NetManager
{
    static NetManager s_instance = new NetManager();
    public static NetManager Instance => s_instance;

    Dictionary<long, INetEvent> netEventHandlers;
    List<INetEvent> netEventHandlerList;
    AService service;

    public void Awake()
    {
        netEventHandlers = new Dictionary<long, INetEvent>(32);
        netEventHandlerList = new List<INetEvent>();
        service = new TService();
        service.AcceptCallback = null;
        service.ReadCallback = this.OnRead;
        service.ErrorCallback = this.OnError;
    }

    public void Destroy()
    {
        service.AcceptCallback = null;
        service.ReadCallback = null;
        service.ErrorCallback = null;
        service.Dispose();
        service = null;
        netEventHandlerList.Clear();
        netEventHandlers.Clear();
    }

    public void Update()
    {
        service.Update();
        foreach (var handle in netEventHandlerList)
        {
            handle.Update();
        }
    }

    public void Connect(INetEvent netEventListener,string ip = "10.41.2.118",int port = 8900,long id = 9527)
    {
        if(netEventHandlers.ContainsKey(id))
        {
            return;
        }
        service.Create(id, new IPEndPoint(IPAddress.Parse(ip), port));
        netEventHandlers[id] = netEventListener;
        netEventHandlerList.Add(netEventListener);
    }

    public void Send(IMessage msg,long id = 9527)
    {
        if(netEventHandlers.TryGetValue(id,out var netEventListener))
        {
            var(outputBuffer,msgLength) = netEventListener.Send(msg);
            if(msgLength > 0)
            {
                var memoryBuffer = service.Fetch();
                memoryBuffer.Write(outputBuffer, 0, msgLength);
                memoryBuffer.Seek(0,System.IO.SeekOrigin.Begin);
                service.Send(id, memoryBuffer);
            }
        }
    }

    void OnRead(long id,MemoryBuffer buffer)
    {
        if(netEventHandlers.TryGetValue(id,out var netEventListener))
        {
            netEventListener.OnRead(buffer);
        }
        service.Recycle(buffer);
    }

    void OnError(long id,int errorCode)
    {
        if (netEventHandlers.TryGetValue(id, out var netEventListener))
        {
            netEventListener.OnNetError(errorCode);
            netEventHandlerList.Remove(netEventListener);
            netEventHandlers.Remove(id);
        }
    }
}
